//resources

const LAB_OWNER_LOGIN = "priemko_smirnova_arnaberdiev_kurs1";

const MP3 = ".mp3";
const PNG = ".png";

const NEXT_QUESTION_SOUND_DURATION = 3000;
const CORRECT_ANSWER_SOUND_DURATION = 1500;
const INCORRECT_ANSWER_SOUND_DURATION = 2000;

const START_GAME_SOUND_NAME = "startGameSound";
const END_GAME_SOUND_NAME = "endGameSound";
const CORRECT_ANSWER_SOUND_NAME = "correctAnswerSound";
const INCORRECT_ANSWER_SOUND_NAME = "incorrectAnswerSound";
const NEXT_QUESTION_SOUND_NAME = "nextQuestionSound";

scene.requireObject(START_GAME_SOUND_NAME + MP3);
scene[START_GAME_SOUND_NAME] = getServerResourceByUser(START_GAME_SOUND_NAME + MP3, LAB_OWNER_LOGIN);
scene.requireObject(END_GAME_SOUND_NAME + MP3);
scene[END_GAME_SOUND_NAME] = getServerResourceByUser(END_GAME_SOUND_NAME + MP3, LAB_OWNER_LOGIN);
scene.requireObject(CORRECT_ANSWER_SOUND_NAME + MP3);
scene[CORRECT_ANSWER_SOUND_NAME] = getServerResourceByUser(CORRECT_ANSWER_SOUND_NAME + MP3, LAB_OWNER_LOGIN);
scene.requireObject(INCORRECT_ANSWER_SOUND_NAME + MP3);
scene[INCORRECT_ANSWER_SOUND_NAME] = getServerResourceByUser(INCORRECT_ANSWER_SOUND_NAME + MP3, LAB_OWNER_LOGIN);
scene.requireObject(NEXT_QUESTION_SOUND_NAME + MP3);
scene[NEXT_QUESTION_SOUND_NAME] = getServerResourceByUser(NEXT_QUESTION_SOUND_NAME + MP3, LAB_OWNER_LOGIN);

const mainRoundsResourcesNames =
    [
        //theme 1
        [4, 3, 4, 4, 4, 3, 3, 4],
        //theme 2
        [4, 4, 4, 4, 3, 6, 4, 6],
        //theme 3
        [4, 4, 3, 4, 4, 3, 3, 4],
        //theme 4
        [4, 3, 3, 3, 4, 4, 4, 4],
        //theme 5
        [4, 4, 4, 4, 4, 4, 4, 4],
        //theme 6
        [4, 4, 4, 4, 4, 4, 4, 4],
        //theme 7
        [4, 4, 4, 4, 4, 4, 4, 4],
        //theme 8
        [4, 4, 4, 4, 4, 4, 4, 4]
    ];

for (var i = 1; i <= mainRoundsResourcesNames.length; ++i)
{
    const questions = mainRoundsResourcesNames[i - 1];
    for (var j = 1; j <= questions.length; ++j)
    {
        const questionSoundName = "nt" + i + "q" + j;
        scene.requireObject(questionSoundName + MP3);
        scene[questionSoundName] = getServerResourceByUser(questionSoundName + MP3, LAB_OWNER_LOGIN);

        const answers = questions[j - 1];
        for (var k = 1; k <= answers; ++k)
        {
            const answerSoundName = questionSoundName + "a" + k;
            scene.requireObject(answerSoundName + MP3);
            scene[answerSoundName] = getServerResourceByUser(answerSoundName + MP3, LAB_OWNER_LOGIN);
        }
    }
}

const finalRoundResourcesNames =
    [
        4, 4, 4, 4, 4, 4, 4, 4
    ];

for (i = 1; i <= finalRoundResourcesNames.length; ++i)
{
    const finalQuestionSoundName = "f" + i;
    scene.requireObject(finalQuestionSoundName + MP3);
    scene[finalQuestionSoundName] = getServerResourceByUser(finalQuestionSoundName + MP3, LAB_OWNER_LOGIN);

    const finalQuestion = finalRoundResourcesNames[i - 1];
    for (j = 1; j <= finalQuestion; ++j)
    {
        const finalAnswerSoundName = finalQuestionSoundName + "a" + j;
        scene.requireObject(finalAnswerSoundName + MP3);
        scene[finalAnswerSoundName] = getServerResourceByUser(finalAnswerSoundName + MP3, LAB_OWNER_LOGIN);
    }
}

const THEMES_COUNT = 8;
const THEME_IMAGE_PREFIX = "ntheme";

const ROUND1_THEMES = [];
const ROUND2_THEMES = [];

for (i = 1; i <= THEMES_COUNT; ++i)
{
    const imgName = THEME_IMAGE_PREFIX + i;
    i <= THEMES_COUNT * 0.5 ? ROUND1_THEMES.push(imgName) : ROUND2_THEMES.push(imgName);

    scene.requireObject(imgName + PNG);
    scene[imgName] = getServerResourceByUser(imgName + PNG, LAB_OWNER_LOGIN);
}

const BEFORE_START_GAME_IMAGE = "before_start_game";
scene.requireObject(BEFORE_START_GAME_IMAGE + PNG);
scene[BEFORE_START_GAME_IMAGE] = getServerResourceByUser(BEFORE_START_GAME_IMAGE + PNG, LAB_OWNER_LOGIN);

const END_OF_GAME_IMAGE = "end_of_game";
scene.requireObject(END_OF_GAME_IMAGE + PNG);
scene[END_OF_GAME_IMAGE] = getServerResourceByUser(END_OF_GAME_IMAGE + PNG, LAB_OWNER_LOGIN);

const START_GAME_AFTER_15_SEC = "start_after_fifteen_sec";
scene.requireObject(START_GAME_AFTER_15_SEC + PNG);
scene[START_GAME_AFTER_15_SEC] = getServerResourceByUser(START_GAME_AFTER_15_SEC + PNG, LAB_OWNER_LOGIN);

// init questions base

const ROUND1_QUESTIONS =
    [
        //theme 1
        [
            [
                ["С помощью какой команды осуществляется самотестирование?", 4500],
                ["Какие процедуры требуется обкладывать юнит-тестами при разделении функциональности и интерфейса?", 7500]
            ],
            [
                ["Обложение каждой процедуры полным набором тестов предполагает", 5500],
                ["В каком случае не следует ставить проверку через Assert?", 4500]
            ],
            [
                ["Сколько минимум должно быть тестов в юнит-тесте?", 4500],
                ["При вводе данных с клавиатуры требуется", 3500]
            ],
            [
                ["Поступающие в программу данные нужно проверять через assert, если:", 5500],
                ["С помощью какого средства тестирования можно поймать возникновение невероятной ситуации внутри программы", 8500]
            ]
        ],
        //theme 2
        [
            [
                ["С помощью какой команды можно узнать размер структуры?", 4500],
                ["Что такое структура?", 2500]
            ],
            [
                ["Что не является особенностью локальных переменных?", 4500],
                ["Выберите из вариантов полный набор функций для выделения памяти", 5500]
            ],
            [
                ["Что такое разыменование?", 2500],
                ["Память под какие переменные выделяется на стеке?", 4500]
            ],
            [
                ["Какой размер указателей типа ^Integer  и ^Byte на 32-битной ОС?", 7500],
                ["За какое кол-во команд минимум можно обменять местами два элемента массива структур?", 7500]
            ]
        ],
        //theme 3
        [
            [
                ["Что такое модуль?", 2500],
                ["Как в Delphi объявить об использовании модуля SampleModule?", 4500]
            ],
            [
                ["Что располагается в секции Implementation модуля?", 4500],
                ["Что НЕ является преимуществом отделения интерфейса от функциональности?", 5500]
            ],
            [
                ["В каком языке модуль состоит из 2 файлов?", 4500],
                ["Может ли быть ввод данных с клавиатуры в процедуре функционала?", 5500]
            ],
            [
                ["В чем заключается идея абстракции данных?", 4500],
                ["Почему юнит-тесты писать проще при разделенных функциональности и интерфейсе?", 6500]
            ]
        ],
        //theme 4
        [
            [
                ["Что такое callback?", 2500],
                ["Какого подхода к организации программ не существует?", 4500]
            ],
            [
                ["Callback бывает", 2500],
                ["Что делает программа, когда нет событий?", 3500]
            ],
            [
                ["В каком случае точно стоит применить callback?", 4500],
                ["Что называется обработчиком события?", 3500]
            ],
            [
                ["Если на форме есть кнопка (не в фокусе) и при активной форме нажали на клавиатуре клавишу, то", 8500],
                ["Выберите правильный вариант объявления callback, который принимает параметром число и возвращает булево значение", 8500]
            ]
        ]
    ];
const ROUND1_ANSWERS =
    [
        //theme 1
        [
            [
                [
                    ["Debug", false],
                    ["Violation", false],
                    ["Assert", true],
                    ["DebugCheck", false]
                ],
                [
                    ["Все", false],
                    ["Только процедуры интерфейса", false],
                    ["Только процедуры функциональности", true]
                ]
            ],
            [
                [
                    ["юнит-тесты", true],
                    ["процедурное программирование", false],
                    ["ручное тестирование", false],
                    ["самотестирование", false]
                ],
                [
                    ["невероятное событие в программе", false],
                    ["неверное значение параметра функции", false],
                    ["проверка в юнит-тесте ", false],
                    ["пользователь ввел с клавиатуры в программу строку вместо числа", true]
                ]
            ],
            [
                [
                    ["сколько параметров в функции", false],
                    ["сколько ветвей исполнения кода в функции", true],
                    ["сколько возможных вариантов параметров функции", false],
                    ["не менее пяти", false]
                ],
                [
                    ["Делать проверку с assert", false],
                    ["Делать обычную проверку и выводишь ошибки, если они есть, на экран", true],
                    ["Не требуется никаких проверок", false]
                ]
            ],
            [
                [
                    ["Только если это цифровые данные", false],
                    ["Если это данные, которые не являются строчками", false],
                    ["Если данные не могут оказаться некорректными по вине пользователя", true]
                ],
                [
                    ["юнит-тесты", false],
                    ["процедурное программирование", false],
                    ["ручное тестирование", false],
                    ["самотестирование", true]
                ]
            ]
        ],
        //theme 2
        [
            [
                [
                    ["Length", false],
                    ["SizeOf", true],
                    ["Count", false],
                    ["MemSize", false]
                ],
                [
                    ["составной тип данных из произвольного числа переменных", true],
                    ["набор из строки и числа", false],
                    ["тип данных для записи и чтения бинарного файла", false],
                    ["строковой тип данных", false]
                ]
            ],
            [
                [
                    ["Память выделяется при входе в процедуру и освобождается на выходе", false],
                    ["Память выделяется на стеке", false],
                    ["Параметры функции тоже случай выделения локальной памяти", false],
                    ["Память выделяется на старте программы и освобождается на выходе", true]
                ],
                [
                    ["New/FreeMem", false],
                    ["GetMem/Dispose", false],
                    ["GetMem/ReallocMem/FreeMem", true],
                    ["GetMem/Dispose/FreeMem", false]
                ]
            ],
            [
                [
                    ["получение номера ячейки, ассоциированной с указателем", false],
                    ["арифметическая операция с указателем", false],
                    ["обращение к значению, на которое указывает указатель", true]
                ],
                [
                    ["Глобальные", false],
                    ["Файловые", false],
                    ["Локальные", true],
                    ["Типизированные", false],
                    ["Выделяемые в динамической памяти", false],
                    ["Временно не используемые", false]
                ]
            ],
            [
                [
                    ["4 и 1 байта соответственно", false],
                    ["4 байта оба", true],
                    ["2 и 1 байта соответственно", false],
                    ["4 и 2 байта соответственно", false]
                ],
                [
                    ["1", false],
                    ["2", false],
                    ["3", true],
                    ["3 * количество полей в структуре", false],
                    ["2 * количество полей в структуре", false],
                    ["количество полей в структуре", false]
                ]
            ]
        ],
        //theme 3
        [
            [
                [
                    ["кусочек исполняемого файла", false],
                    ["объявления функций", false],
                    ["отдельный исходный файл", true],
                    ["файл с документацией ", false]
                ],
                [
                    ["Unit SampleModule", false],
                    ["Uses SampleModule", true],
                    ["UseUnit SampleModule", false],
                    ["#include “SampleModule.h”", false]
                ]
            ],
            [
                [
                    ["Все функции, которые нельзя вызвать извне", false],
                    ["Реализация всех процедур и функций, а также переменные, константы, типы данных, не нужные извне", true],
                    ["Функции и процедуры, которые нужны для вызова извне, а также связанные с ними переменные", false]
                ],
                [
                    ["Проще развивать проект", false],
                    ["Нет утечек памяти", true],
                    ["Экономия ресурсов", false],
                    ["Проще писать юнит-тесты", false]
                ]
            ],
            [
                [
                    ["Pascal", false],
                    ["PHP", false],
                    ["Delphi", false],
                    ["C/C++", true]
                ],
                [
                    ["Да", false],
                    ["Нет", true],
                    ["Может, если требуется уточнение данных, введенных пользователем", false]
                ]
            ],
            [
                [
                    ["разделение функциональности и интерфейса", false],
                    ["разделение программы на модули", false],
                    ["разделении несущественных деталей реализации модуля и характеристик существенных для корректного его использования", true]
                ],
                [
                    ["юнит-тестами надо обкладывать только процедуры функционала", true],
                    ["тесты на некорректные значения данных писать не надо", false],
                    ["можно ограничиться меньшим числом тестов", false],
                    ["на тестирование процедур интерфейса нужно вдвое меньше тестов", false]
                ]
            ]
        ],
        //theme 4
        [
            [
                [
                    ["запрос на обратный звонок", false],
                    ["вызов процедуры", false],
                    ["функция обратного вызова", true],
                    ["выход из процедуры", false]
                ],
                [
                    ["последовательное программирование", false],
                    ["классовое программирование", true],
                    ["событийное программирование", false]
                ]
            ],
            [
                [
                    ["синхронным и асинхронным", true],
                    ["целочисленным и строковым", false],
                    ["реализованным и нереализованным", false]
                ],
                [
                    ["ничего (спит)", true],
                    ["выполняет функции обратного вызова", false],
                    ["выполняет модули интерфейса", false]
                ]
            ],
            [
                [
                    ["было проведено разделение на функциональность и интерфейс", false],
                    ["у функции более двух параметров", false],
                    ["нецелесообразно возвращать все результаты разом", true],
                    ["в модуле более двух функций", false]
                ],
                [
                    ["переменные, которые является параметрами события", false],
                    ["процедура, которая вызывается, чтобы событие произошло", false],
                    ["процедура или функция, которая вызывается, когда событие происходит", true],
                    ["процедура, изменяющая параметры события", false]
                ]
            ],
            [
                [
                    ["Событие OnKeyDown придет кнопке", false],
                    ["Событие OnKeyDown придет форме", true],
                    ["Событие OnKeyDown придет форме и кнопке", false],
                    ["Событие OnKeyDown не придет ни кнопке, ни форме", false]
                ],
                [
                    ["int(*getValue)(bool)", false],
                    ["bool(*getValue)(int)", true],
                    ["bool getValue(int)", false],
                    ["bool* getValue(int)", false]
                ]
            ]
        ]
    ];

const ROUND2_QUESTIONS =
    [
        //theme 5
        [
            [
                ["Что такое библиотеки?", 2500],
                ["Чем отличаются статические библиотеки от динамических?", 4500]
            ],
            [
                ["Что НЕ является преимуществом статических библиотек?", 4500],
                ["Что НЕ является вариантом использования DLL?", 3500]
            ],
            [
                ["Что НЕ является недостатком динамических библиотек?", 3500],
                ["Могут ли функции обратного вызова использоваться вместе с DLL?", 4500]
            ],
            [
                ["Могут ли функции обратного вызова использоваться вместе с DLL?", 8500],
                ["Есть программе, которая использует статическую и динамическую библиотеку. В динамической библиотеке выделена память. Где она может быть освобождена?", 10500]
            ]
        ],
        //theme 6
        [
            [
                ["Что относится к окнам в понятии WinAPI?", 3500],
                ["Что такое сообщение в WinAPI?", 2500]
            ],
            [
                ["Обработчики сообщения находятся в:", 2500],
                ["Как нельзя найти окно через FindWindow?", 3500]
            ],
            [
                ["Выберите сообщение, которое приходит на закрытии окна", 4500],
                ["Выберите сообщение, которое приходит при нажатии на кнопку", 4500]
            ],
            [
                ["Сколько данных максимум можно переслать через оконное сообщение?", 4500],
                ["Чего нет у окон (форм)", 2500]
            ]
        ],
        //theme 7
        [
            [
                ["Что НЕ может являться причиной Stack Overflow?", 3500],
                ["Какие последствия имеет порча памяти?", 3500]
            ],
            [
                ["Что НЕ может являться причиной порчи памяти?", 3500],
                ["Какой из способов выделения памяти НЕ является выделением на стеке?", 5500]
            ],
            [
                ["Каким образом принудительно освободить память выделенную через alloca?", 4500],
                ["Для переменной short* a вызов a++ приведет к увеличению адреса на", 6500]
            ],
            [
                ["Выберите из вариантов ответов указатель на указатель", 3500],
                ["Для переменной char* a; выражение &*a это", 6500]
            ]
        ],
        //theme 8
        [
            [
                ["Выберите объект STL для работы со строками", 3500],
                ["Выберите объект STL для работы с обычным массивом", 3500]
            ],
            [
                ["Как превратить std::string s в char* a?", 4500],
                ["Как привести std::vector<int> a к int* b?", 5500]
            ],
            [
                ["Что не входит в состав STL?", 2500],
                ["Что такое итератор?", 1500]
            ],
            [
                ["Как ускорить работу с std::vector?", 2500],
                ["Что такое реверсивный итератор?", 2500]
            ]
        ]
    ];
const ROUND2_ANSWERS =
    [
        //theme 5
        [
            [
                [
                    ["отдельный исходный файл", false],
                    ["пара файлов: исходный файл и файл с заголовками функций", false],
                    ["отдельная программная часть, состоящая из одного или нескольких исходных файлов", true],
                    ["отдельная программная часть, состоящая из пары файлов: исходный файл и файл с заголовками функций", false]
                ],
                [
                    ["у статических код функций включается в ЕХЕ-файл", true],
                    ["у статических код функций включается в отдельный файл и разделяется многими ЕХЕ-файлами во время исполнения программ", false],
                    ["у статических код функций не включается в ЕХЕ-файл", false],
                    ["вариант A и B", false]
                ]
            ],
            [
                [
                    ["выделять и освобождать память можно не задумываясь о том, где она выделена", false],
                    ["нет проблем с разными версиями библиотек", false],
                    ["размер продукта сокращается при использовании библиотеки во многих ЕХЕ-файлах", true],
                    ["вариант A и B", false]
                ],
                [
                    ["сокращение размера продукта", false],
                    ["плагины", false],
                    ["оптимизация скорости выполнения кода", true],
                    ["разные реализации одной и той же функциональности", false]
                ]
            ],
            [
                [
                    ["сложность управления памятью", false],
                    ["может быть не загружена DLL", false],
                    ["программный код функций DLL включается в сам EXE-файл", true],
                    ["может быть не найдена нужная функция в DLL", false]
                ],
                [
                    ["Да, DLL могут вызывать функции обратного вызова, реализованные в основной программе", false],
                    ["Да, основная программа может вызывать функции обратного вызова, реализованные в DLL", false],
                    ["Нет", false],
                    ["Вариант А и B", true]
                ]
            ],
            [
                [
                    ["Только в самой программе", false],
                    ["Только в статической библиотеке", false],
                    ["Только в статической библиотеке и в самой программе", true],
                    ["И в программе, и в обоих видах библиотек", false]
                ],
                [
                    ["Только в самой программе", false],
                    ["Только в динамической библиотеке", true],
                    ["Только в динамической библиотеке и в самой программе", false],
                    ["И в программе, и в обоих видах библиотек", false]
                ]
            ]
        ],
        //theme 6
        [
            [
                [
                    ["формы", false],
                    ["кпопки и поля ввода", false],
                    ["и формы, и все элементы управления", true],
                    ["заголовки окна", false]
                ],
                [
                    ["то, что выводит MessageBox", false],
                    ["синоним события", false],
                    ["специальный пакет данных, который пересылается от окна к окну и от системы к окну", true],
                    ["две цифры по 4 байта для пересылки", false]
                ]
            ],
            [
                [
                    ["цикле обработки сообщений", false],
                    ["оконной процедуре", true],
                    ["WinMain", false],
                    ["процедуре-дескрипторе", false]
                ],
                [
                    ["по заголовку", false],
                    ["по имени кнопки", true],
                    ["по классу", false],
                    ["по заголовку и классу одновременно", false]
                ]
            ],
            [
                [
                    ["WM_CREATE", false],
                    ["WM_COMMAND", false],
                    ["WM_MOUSEMOVE", false],
                    ["WM_DESTROY", true]
                ],
                [
                    ["WM_CREATE", false],
                    ["WM_MOUSEMOVE", false],
                    ["WM_DESTROY", false],
                    ["WM_KEYDOWN", true]
                ]
            ],
            [
                [
                    ["2 байта", false],
                    ["4 байта", false],
                    ["8 байт", false],
                    ["Любой разумный объем данных с помощью WM_COPYDATA", true]
                ],
                [
                    ["размера", false],
                    ["заголовка", false],
                    ["положения относительно родительского окна", false],
                    ["предопределенного класса", true]
                ]
            ]
        ],
        //theme 7
        [
            [
                [
                    ["слишком большие локальные переменные", false],
                    ["слишком большая вложенность рекурсии", false],
                    ["слишком большие глобальные переменные", false],
                    ["вариант А и В вместе", true]
                ],
                [
                    ["медленная работа на загрузке", false],
                    ["инкрементация указателя", false],
                    ["разыменование указателя", false],
                    ["падение программы", true]
                ]
            ],
            [
                [
                    ["обращение по неинициализированному указателю", false],
                    ["выход за границы выделенной памяти", false],
                    ["использование стека", true],
                    ["удаление памяти по уже удаленному указателю", false]
                ],
                [
                    ["alloca", false],
                    ["объявление локальной переменной", false],
                    ["new", true],
                    ["использование параметра функции", false]
                ]
            ],
            [
                [
                    ["delete", false],
                    ["delete[]", false],
                    ["free", false],
                    ["никак", true]
                ],
                [
                    ["1 байт", false],
                    ["2 байта", true],
                    ["4 байта", false],
                    ["адрес не изменится", false]
                ]
            ],
            [
                [
                    ["**str", false],
                    ["char** str", true],
                    ["char* str", false],
                    ["char* char* str", false]
                ],
                [
                    ["указатель на указатель на переменную а", false],
                    ["значение переменной a", true],
                    ["адрес указателя на переменную а", false],
                    ["указатель на переменную а", false]
                ]
            ]
        ],
        //theme 8
        [
            [
                [
                    ["std::string", true],
                    ["std::vector", false],
                    ["std::list", false],
                    ["std::list", false]
                ],
                [
                    ["std::string", false],
                    ["std::vector", true],
                    ["std::list", false],
                    ["std::map", false]
                ]
            ],
            [
                [
                    ["a = s.c_str()", true],
                    ["a = s", false],
                    ["a = &s", false],
                    ["a = *s", false]
                ],
                [
                    ["b = &a[0];", true],
                    ["b = a", false],
                    ["b = *a", false],
                    ["b = a++", false]
                ]
            ],
            [
                [
                    ["Контейнеры", false],
                    ["Итераторы", false],
                    ["Обобщенные алгоритмы", false],
                    ["Двойные указатели", true]
                ],
                [
                    ["Синоним указател", false],
                    ["Специальный указатель на элемент STL-контейнера", true],
                    ["Операция разыменования элемента массива ссылок", false],
                    ["Двойной указатель на элемент std::vector", false]
                ]
            ],
            [
                [
                    ["Резервировать память", false],
                    ["Добавлять элементы только через insert", false],
                    ["Использовать итераторы", false],
                    ["Варианты А и С вместе", true]
                ],
                [
                    ["Итератор, который может перемещаться по STL-контейнеру в обоих направлениях", false],
                    ["Итератор, который предназначен для обхода STL-контейнера с конца", true],
                    ["Двойной указатель на элемент std::vector", false],
                    ["Операция разыменования элемента массива ссылок", false]
                ]
            ]
        ]
    ];

const FINAL_ROUND_QUESTIONS =
    [
        ["Напишите как задать в языке С++ переменную с названием k типа указатель на массив указателей на int.", 8500],
        ["Выберите причину, из-за которых может появляться ошибка Stack Overflow.", 5500],
        ["Чем строка отличается от массива байт в языках С/С++?", 4500],
        ["Чем модуль отличается от библиотеки?", 2500],
        ["Чем отличается схема модулей от схемы алгоритма?", 3500],
        ["Что такое порча памяти?", 2500],
        ["Чем событие отличается от оконного сообщения?", 3500],
        ["Какой объект является ассоциативным массивом?", 3500]
    ];
const FINAL_ROUND_ANSWERS =
    [
        [
            ["int** k", false, 2500],
            ["int *** k", true, 3500],
            ["int * k", false, 2500],
            ["&int* k", false, 2500]
        ],
        [
            ["Слишком длинное название функции", false, 2500],
            ["Слишком длинное название функции", true, 2500],
            ["Слишком большая глубина рекурсии", false, 2500],
            ["Слишком большие глобальные переменные", false, 2500]
        ],
        [
            ["Ничем", false, 1500],
            ["Строка это указатель на char, массив указатель не является", false, 4500],
            ["Финальным нулем в конце массива", false, 2500],
            ["Финальным нулем в конце строки", true, 2500]
        ],
        [
            ["В модуле два файла, в библиотеке 1", false, 3500],
            ["В библиотеке может быть несколько модулей внутри", true, 3500],
            ["Внутри модуля может быть несколько библиотек", false, 3500],
            ["Это синонимы", false, 3500]
        ],
        [
            ["Схема модулей это схема последовательности действий", false, 3500],
            ["Схема алгоритма показывает деление программы на модули", false, 3500],
            ["Схема модулей показывает деление на модули, схема алгоритма показывает последовательность шагов", true, 6500],
            ["Это одно и то же", false, 2500]
        ],
        [
            ["Ошибочное изменение области памяти", true, 3500],
            ["Выгорание ячеек памяти", false, 2500],
            ["Это вид утечки памяти", false, 2500],
            ["Это попытка приведения указателей", false, 2500]
        ],
        [
            ["Оконное сообщение может передавать как событие, так и что-то другое.", true, 4500],
            ["Событие может быть аналогом оконного сообщения, а может быть аналогом чего-либо другого.", false, 6500],
            ["Событие посылается от окна к окну, а оконное сообщение приходит только от системы.", false, 5500],
            ["Это одно и то же.", false, 2500]
        ],
        [
            ["std::string", false, 2500],
            ["std::list", false, 1500],
            ["std::vector", false, 1500],
            ["std::map", true, 1500]
        ]
    ];

//models

function Answer()
{
    this.text = "";
    this.sound = null;
    this.soundDuration = 0;
    this.isCorrect = "";
}

Answer.prototype.text;
Answer.prototype.sound;
Answer.prototype.soundDuration;
Answer.prototype.isCorrect;

function Question()
{
    this.text = "";
    this.answers = [];
    this.score = 0;
    this.isChoosen = false;
    this.sound = null;
    this.soundDuration = 0;

    this.getAnswersTexts = function()
    {
        var texts = [];
        for (var i = 0; i < this.answers.length; ++i)
        {
            texts.push(this.answers[i].text);
        }
        return texts;
    };

    this.getAnswersSounds = function()
    {
        var sounds = [];
        for (var i = 0; i < this.answers.length; ++i)
        {
            sounds.push(this.answers[i].sound);
        }
        return sounds;
    };
}
Question.prototype.text;
Question.prototype.answers;
Question.prototype.score;
Question.prototype.isChoosen;
Question.prototype.sound;
Question.prototype.soundDuration;
Question.prototype.getAnswersTexts;

function Theme()
{
    this.questions = [];
}
Theme.prototype.questions;

// classes

function Timer(functionName, delay)
{
    this._functionName = functionName;
    this._delay = delay;
    this._isStoped = false;
    this._isStarted = false;
    this._id = Timer._counter++;
}

Timer.prototype._functionName;
Timer.prototype._delay;
Timer.prototype._isStoped;
Timer.prototype._isStarted;
Timer.prototype._id;
Timer._counter = 0;
Timer._timerFuncPrefix = "timerId";

Timer.prototype.start = function()
{
    if (this._isStarted)
    {
        return;
    }

    this._isStoped = false;
    this._isStarted = true;

    var funcName = Timer._timerFuncPrefix + this._id;
    var thisPtr = this;
    scene[funcName] = function()
    {
        if (!thisPtr._isStoped)
        {
            scene[thisPtr._functionName]();
        }
        thisPtr._isStarted = false;
    };

    playByTimer(funcName, this._delay);
};

Timer.prototype.stop = function()
{
    this._isStoped = true;
};

// consts and enums

const GAME_STATE =
{
    NOT_STARTED: "notStarted",
    BEFORE_STARTED: "beforeStarted",
    STARTED: "started",
    ENDED: "ended"
};

const CLEAR_RECORDS_TABLE_PASS = "3aa37e9be529987321b636ec433ba69f5645f189";
const RECORDS_TABLE_ROWS_COUNT = 10;
const GAME_NAME = "e3af39a63670756d646b15ee4810f6a77723f084";
const TABLE_CELL_ROTATE_ANGLE = 1;
const DEFAULT_Z_POS = 731;
const DEFAULT_READY_BUTTON_Z_POS = 810;
const DEFAULT_SITTING_PLACE_Z_POS = 800;
const TABLE_CELL_START_POS = new Position3D(245601, 75953, DEFAULT_Z_POS);
const TABLE_CELL_SIDE_SIZE = 141;
const TABLE_ROWS_COUNT = 4;
const TABLE_COLS_COUNT = 5;
const MAIN_ROUNDS_COUNT = 2;
const FINAL_ROUND_INDEX = 3;
const QUESTIONS_COUNT_FOR_EACH_ROUND = TABLE_ROWS_COUNT * (TABLE_COLS_COUNT - 1);
const QUESTIONS_FIELD_COLS_COUNT = TABLE_COLS_COUNT - 1;
const QUESTION_COUNT_FOR_EACH_SCORE = 2;
const SCORE_STEP = 25;
const ANCHORMAN_BOT_NAME = "anchorman";
const SECRET_SEPARATOR = "~";
const SOLVED_QUESTION_COORDS_SEPARATOR = ",";
const MY_AVATAR_NAME = getMyAvatarName();
const MY_AVATAR_FIRST_AND_LAST_NAME = getMyAvatarFirstAndLastName();
const BEFORE_START_SOLVE_QUESTIONS_TIME = 15000;
const DEFAULT_WAITING_TIME = 30000;
const FINAL_ROUND_TIME = 45000;
const DEFAULT_CHECK_INTERVAL = 500;
const TABLE_ITEM_NAME_PREFIX = "cube";
const SCORESHEET_MAX_PLAYER_NAME_LENGTH = 15;
const QUESTION_TEXT_FONT_SIZE = 14;
const QUESTION_TEXT_LINE_SIZE = 27;
const QUESTION_TEXT_PATTERN = new RegExp("([\\S\\s]{" + QUESTION_TEXT_LINE_SIZE + "})", "g");
const TRUE = "true";
const FALSE = "false";
const DEBUG_MODE = false;
const TEXTURES_SIZE = 256;

const gameNotStartedWelcomePlayerText = "Игра еще не началась. Вы можете занять место участника или быть зрителем";
const gameStartedWelcomePlayerText = "Игра уже началась. Вы можете занять место зрителя";
const welcomeWindowTitle = "Добро пожаловать!";
const requestPassToClearRecordsTableMessage = "Введите пароль:";
const finalQuestionWindowTitle = "Финальный вопрос";
const finalQuestionInfo = "При ответе сначала впишите номер ответа, затем через пробел сумму баллов, которую вы ставите на свой ответ";
const requestPassToClearRecordsTableWindowTitle = "Очистить таблицу рекордов";
const clearRecordsTableFeedbackWindowTitle = "Очистка таблицы рекордов";
const clearRecordsTableSuccessFeedback = "Таблица очищена";
const clearRecordsTableFailFeedback = "Неверный пароль очистки таблицы рекордов";
const startingGameWindowMessage = "Игра начнется через 15 секунд!";
const startingGameWindowTitle = "Внимание";
const chooseQuestionInvitation = "Выбирайте вопрос";

const PLAYER_NOT_ANSWERED_FUNC = "playerNotAnswered";
const NOBODY_WANTS_ANSWER_FUNC = "nobodyWantsAnswer";
const CLOSE_FINAL_ROUND_INPUT_BOX_FUNC = "closeFinalRoundInputBox";
const CHECK_SITTING_PLACES_FUNC = "checkSittingPlaces";
const CHECK_SHOW_RECORDS_TABLE_AT_THE_END_OF_GAME = "showRecordsTableAtTheEndOfGame";
const CHECK_SHOW_START_GAME_MESSAGE = "checkShowStartGameMessage";
const START_SOLVE_QUESTIONS_FUNC = "startSolveQuestions";
const FINAL_ROUND_TIMER_CALLBACK = "finalRoundTimerCallback";
const CHECK_ADD_RESULTS_TO_SCORE_TABLE = "checkAddResultsToScoreTable";
const CHECK_RESET_FLAGS = "checkResetFlags";
const CHECK_SHOW_FINAL_QUESTION = "checkShowFinalQuestion";

const GO_TO_NEXT_QUESTION_ANNOUNCED = "goToNextQuestionAnnounced";
const ANNOUNCE_CORRECT_ANSWER_AFTER_FINAL_ROUND = "announceCorrectAnswerAfterFinalRound";
const QUESTION_SOUND_END = "questionSoundEnd";
const IS_CORRECT_ANSWER_SOUND_END = "isCorrectAnswerSoundEnd";
const FINAL_ROUND_ENDED_SOUND_END = "finalRoundEndedSoundEnd";

//vars

const synchronizer = scene.getObjectByName("synchronizer");
const showRecordsTableObject = scene.getObjectByName("showRecordsTable");
const clearRecordsTableObject = scene.getObjectByName("clearRecordsTable");
const questionTextObject = scene.getObjectByName("questionText");
const readyAnswerButton = scene.getObjectByName("readyAnswerButton");
const scoresheet = scene.getObjectByName("scoresheet");
const chair1 = scene.getObjectByName("chair1");
const chair2 = scene.getObjectByName("chair2");
const chair3 = scene.getObjectByName("chair3");
const anchormanBot = scene.getBotByName(ANCHORMAN_BOT_NAME);

const round1QuestionsGroups = [];
const round2QuestionsGroups = [];
const finalRoundQuestions = [];
const tableCubes = [];
const questionCubes = [];
const sittingPlaces = [chair1, chair2, chair3];

var startingGameMessageBoxIsShown = false;

var startGameTimer;
var finalRoundInputBox;
var receivingAnswerDialogue;
var startingGameMessageBox;
var finalRoundCorrectAnswer;
var isCorrectAnswerForTimer;
var currentQuestion;
var needDestroyTimers = true;

//init

initRecordsTable();
initShowRecordsTableObject();
initClearRecordsTableObject();

initRoundQuestions(round1QuestionsGroups, ROUND1_QUESTIONS, ROUND1_ANSWERS, true);
initRoundQuestions(round2QuestionsGroups, ROUND2_QUESTIONS, ROUND2_ANSWERS, false);
initFinalRoundQuestions();

initTableCubes();
startListenReadyAnswerButton();

initializeGame();

initQuestionCubesHandlers();

initializingComplete();

//script

showWelcomeWindow();

playInCycle(CHECK_SITTING_PLACES_FUNC, DEFAULT_CHECK_INTERVAL);

playInCycle(CHECK_ADD_RESULTS_TO_SCORE_TABLE, DEFAULT_CHECK_INTERVAL);
playInCycle(CHECK_SHOW_RECORDS_TABLE_AT_THE_END_OF_GAME, DEFAULT_CHECK_INTERVAL);
playInCycle(CHECK_SHOW_START_GAME_MESSAGE, DEFAULT_CHECK_INTERVAL);
playInCycle(CHECK_RESET_FLAGS, DEFAULT_CHECK_INTERVAL);
playInCycle(CHECK_SHOW_FINAL_QUESTION, DEFAULT_CHECK_INTERVAL);

// functions

function initializeGame()
{
    initSynchronizer();

    if (isNeedVisualElementsInitialize())
    {
        initTableCubesAngle();
        initTableCubesPos();
        initSittingPlacesZPos();

        initTexturesForCurrRound(true);

        initReadyAnswerButtonEnabledTexture();

        generateQuestionTableImageComposition(questionTextObject, scene[BEFORE_START_GAME_IMAGE]);

        updateScoreSheet();
    }
}

function initializingComplete()
{
    if (synchronizer.synch.initializingCapturedAvatar == MY_AVATAR_NAME)
    {
        synchronizer.synch.commonPartInitialized = TRUE;
    }
}

function initSittingPlacesZPos()
{
    for (var i = 0; i < sittingPlaces.length; ++i)
    {
        sittingPlaces[i].position.z = DEFAULT_SITTING_PLACE_Z_POS;
    }
}

function startListenReadyAnswerButton()
{
    readyAnswerButton.onPress = mainRoundReadyButtonClickHandler;
}

function ignoreReadyAnswerButton()
{
    readyAnswerButton.onPress = function(){};
}

function mainRoundReadyButtonClickHandler()
{
    if ((synchronizer.synch.answeringPlayerName == SECRET_SEPARATOR) && (synchronizer.synch.currentQuestionIndexes != SECRET_SEPARATOR) && !isSpectatorAvatarClick())
    {
        synchronizer.synch.answeringPlayerName = MY_AVATAR_NAME;

        showAnswerOptions();
    }
}

function initReadyAnswerButtonEnabledTexture()
{
    readyAnswerButton.position.z = DEFAULT_READY_BUTTON_Z_POS;
}

function initQuestionCubesHandlers()
{
    for (var i = 0; i < TABLE_ROWS_COUNT; ++i)
    {
        for (var j = 0; j < QUESTIONS_FIELD_COLS_COUNT; ++j)
        {
            const currCube = questionCubes[i][j];
            initQuestionCubeHandler(currCube, i, j);
        }
    }
}

function initQuestionCubeHandler(cube, i, j)
{
    cube.onPress = function()
    {
        if ((synchronizer.synch.ignoreQuestionCubeClick == TRUE) || isIgnoreClickOnQuestionCube(i, j))
        {
            return;
        }

        ignoreReadyAnswerButton();

        const randomQuestionIndex = getRandom() % QUESTION_COUNT_FOR_EACH_SCORE;
        const questionsGroups = getCurrQuestionsGroups();

        const question = questionsGroups[i].questions[j][randomQuestionIndex];

        synchronizer.synch.currentQuestionIndexes = [i, j, randomQuestionIndex].join(SECRET_SEPARATOR);
        synchronizer.synch.solvedQuestionsIndexes += [i, j].join(SOLVED_QUESTION_COORDS_SEPARATOR) + SECRET_SEPARATOR;

        generateQuestionCubeTexture(cube, "", j);
        generateQuestionTableTextComposition(questionTextObject, question.text);

        const timer = new Timer(QUESTION_SOUND_END, question.soundDuration);
        anchormanBot.playSound(question.sound, 100);
        timer.start();
    }
}

scene[QUESTION_SOUND_END] = function()
{
    startListenReadyAnswerButton();

    const waitingPlayerReadyAnswerTimer = new Timer(NOBODY_WANTS_ANSWER_FUNC, DEFAULT_WAITING_TIME);
    waitingPlayerReadyAnswerTimer.start();
};

function showAnswerOptions()
{
    if (synchronizer.synch.answeringPlayerName == MY_AVATAR_NAME)
    {
        ++synchronizer.synch.waitingPlayerReadyAnswerTimerBreakesCount;

        const answerTimer = new Timer(PLAYER_NOT_ANSWERED_FUNC, DEFAULT_WAITING_TIME);
        answerTimer.start();

        const questionIndexes = synchronizer.synch.currentQuestionIndexes.split(SECRET_SEPARATOR);
        const currQuestionGroup = getCurrQuestionsGroups();
        currentQuestion = currQuestionGroup[questionIndexes[0]].questions[questionIndexes[1]][questionIndexes[2]];

        receivingAnswerDialogue = showDialogueBox(ANCHORMAN_BOT_NAME, currentQuestion.text, currentQuestion.getAnswersTexts(), null, currentQuestion.getAnswersSounds());
        receivingAnswerDialogue.onClose = function()
        {
            answerTimer.stop();

            if (synchronizer.synch.gameState == GAME_STATE.ENDED)
            {
                return;
            }

            const buttonId = this.getButtonId();
            isCorrectAnswerForTimer = false;
            if (buttonId != 0)
            {
                const answer = currentQuestion.answers[buttonId - 1];
                isCorrectAnswerForTimer = answer.isCorrect;
            }

            const timer = new Timer(IS_CORRECT_ANSWER_SOUND_END, isCorrectAnswerForTimer ? CORRECT_ANSWER_SOUND_DURATION : INCORRECT_ANSWER_SOUND_DURATION);
            anchormanBot.playSound(isCorrectAnswerForTimer ? scene[CORRECT_ANSWER_SOUND_NAME] : scene[INCORRECT_ANSWER_SOUND_NAME], 100);
            timer.start();
        }
    }
}

scene[IS_CORRECT_ANSWER_SOUND_END] = function()
{
    changeMyAvatarScore(isCorrectAnswerForTimer, currentQuestion.score);

    if (isCorrectAnswerForTimer)
    {
        prepareToSelectNextQuestion();
    }
    else
    {
        const waitingPlayerReadyAnswerTimer = new Timer(NOBODY_WANTS_ANSWER_FUNC, DEFAULT_WAITING_TIME);
        waitingPlayerReadyAnswerTimer.start();
    }

    updateScoreSheet();

    synchronizer.synch.answeringPlayerName = SECRET_SEPARATOR;

    isCorrectAnswerForTimer = null;
    currentQuestion = null;
};

scene[PLAYER_NOT_ANSWERED_FUNC] = function()
{
    receivingAnswerDialogue.close();
};

scene[NOBODY_WANTS_ANSWER_FUNC] = function()
{
    if (synchronizer.synch.waitingPlayerReadyAnswerTimerBreakesCount != 0)
    {
        --synchronizer.synch.waitingPlayerReadyAnswerTimerBreakesCount;
    }
    else
    {
        prepareToSelectNextQuestion();
    }
};

function initTexturesForCurrRound(clearTable)
{
    const themesImages = getCurrThemesImages();
    for (var i = 0; i < TABLE_ROWS_COUNT; ++i)
    {
        generateThemeCubeTexture(tableCubes[i][0], clearTable ? null : scene[themesImages[i]]);

        for (var j = 0; j < QUESTIONS_FIELD_COLS_COUNT; ++j)
        {
            generateQuestionCubeTexture(questionCubes[i][j], clearTable ? "" : ("" + (j + 1) * SCORE_STEP), j);
        }
    }
}

function initFinalRound()
{
    destroyTimers();
    needDestroyTimers = false;

    readyAnswerButton.position.z = -DEFAULT_READY_BUTTON_Z_POS;

    for (var i = 0; i < TABLE_ROWS_COUNT; ++i)
    {
        generateThemeCubeTexture(tableCubes[i][0], null);
    }
    initTexturesForCurrRound(true);

    const finalQuestionIndex = getRandom() % FINAL_ROUND_QUESTIONS.length;
    synchronizer.synch.finalQuestionIndex = finalQuestionIndex;
    const finalQuestion = finalRoundQuestions[finalQuestionIndex];

    anchormanBot.playSound(finalQuestion.sound, 100);

    generateQuestionTableTextComposition(questionTextObject, finalQuestion.text);

    const finalQuestionTimer = new Timer(FINAL_ROUND_TIMER_CALLBACK, FINAL_ROUND_TIME);
    finalQuestionTimer.start();

    synchronizer.synch.finalRoundStarted = TRUE;
}

function destroyTimers()
{
    for (var i = Timer._counter; i >= 0; --i)
    {
        scene[Timer._timerFuncPrefix + i] = function(){};
    }
}

function showFinalRoundQuestion()
{
    const finalQuestion = finalRoundQuestions[synchronizer.synch.finalQuestionIndex];

    var questionText = finalQuestionInfo + "<br /><br />";
    const answers = finalQuestion.getAnswersTexts();
    for (var j = 0; j < answers.length; ++j)
    {
        questionText += (j + 1) + ". " + answers[j] + "<br />";
    }

    finalRoundInputBox = showInputBox(finalQuestionWindowTitle, questionText);

    const closeFinalRoundInputBoxTimer = new Timer(CLOSE_FINAL_ROUND_INPUT_BOX_FUNC, FINAL_ROUND_TIME);
    closeFinalRoundInputBoxTimer.start();

    finalRoundInputBox.onNo = function()
    {
        closeFinalRoundInputBoxTimer.stop();
    };

    finalRoundInputBox.onCancel = function()
    {
        closeFinalRoundInputBoxTimer.stop();
    };

    finalRoundInputBox.onYes = function()
    {
        closeFinalRoundInputBoxTimer.stop();

        const input = finalRoundInputBox.getInput().split(" ");
        if (input.length != 2 || !isValidNumber(input[0]) || !isValidNumber(input[1]))
        {
            return;
        }

        const answerNumber = parseInt(input[0]);
        const answers = finalQuestion.answers;
        if (answerNumber < 1 || answerNumber > answers.length)
        {
            return;
        }

        const myAvatarIndex = getMyAvatarIndex();
        const scores = synchronizer.synch.scores.split(SECRET_SEPARATOR);
        const score = Math.max(0, Math.min(parseInt(scores[myAvatarIndex]), parseInt(input[1])));

        changeMyAvatarScore(answers[answerNumber - 1].isCorrect, score);
    };
}

scene[CLOSE_FINAL_ROUND_INPUT_BOX_FUNC] = function()
{
    if (finalRoundInputBox)
    {
        finalRoundInputBox.close();
    }
};

scene[FINAL_ROUND_TIMER_CALLBACK] = function()
{
    if (synchronizer.synch.finalQuestionAnswerSounded == FALSE)
    {
        synchronizer.synch.finalQuestionAnswerSounded = TRUE;

        readyAnswerButton.position.z = DEFAULT_READY_BUTTON_Z_POS;

        const finalQuestion = finalRoundQuestions[synchronizer.synch.finalQuestionIndex];
        const answers = finalQuestion.answers;

        for (var i = 0; i < answers.length; ++i)
        {
            const currAnswer = answers[i];
            if (currAnswer.isCorrect)
            {
                finalRoundCorrectAnswer = currAnswer;
                break;
            }
        }

        const timer = new Timer(ANNOUNCE_CORRECT_ANSWER_AFTER_FINAL_ROUND, CORRECT_ANSWER_SOUND_DURATION);
        anchormanBot.playSound(scene[CORRECT_ANSWER_SOUND_NAME], 100);
        timer.start();
    }
};

scene[ANNOUNCE_CORRECT_ANSWER_AFTER_FINAL_ROUND] = function()
{
    if (synchronizer.synch.gameState != GAME_STATE.STARTED)
    {
        return;
    }

    const timer = new Timer(FINAL_ROUND_ENDED_SOUND_END, finalRoundCorrectAnswer.soundDuration);
    anchormanBot.playSound(finalRoundCorrectAnswer.sound, 100);
    timer.start();
};

scene[FINAL_ROUND_ENDED_SOUND_END] = function()
{
    endGame();
};

function getChooseQuestionInvitation()
{
    return synchronizer.synch.choosingQuestionPlayerFullName + ", " + chooseQuestionInvitation;
}

scene[GO_TO_NEXT_QUESTION_ANNOUNCED] = function()
{
    if (synchronizer.synch.gameState != GAME_STATE.STARTED)
    {
        return;
    }

    synchronizer.synch.ignoreQuestionCubeClick = FALSE;

    generateQuestionTableTextComposition(questionTextObject, getChooseQuestionInvitation());

    if (synchronizer.synch.solvedQuestionsCount == QUESTIONS_COUNT_FOR_EACH_ROUND)
    {
        synchronizer.synch.solvedQuestionsCount = 0;
        synchronizer.synch.solvedQuestionsIndexes = "";
        ++synchronizer.synch.currRound;

        if (synchronizer.synch.currRound == FINAL_ROUND_INDEX)
        {
            initFinalRound();
        }
        else
        {
            initTexturesForCurrRound(false);
        }
    }
};

function prepareToSelectNextQuestion()
{
    synchronizer.synch.ignoreQuestionCubeClick = TRUE;

    const timer = new Timer(GO_TO_NEXT_QUESTION_ANNOUNCED, NEXT_QUESTION_SOUND_DURATION);
    anchormanBot.playSound(scene[NEXT_QUESTION_SOUND_NAME], 100);
    timer.start();

    ++synchronizer.synch.solvedQuestionsCount;
    synchronizer.synch.currentQuestionIndexes = SECRET_SEPARATOR;
    synchronizer.synch.choosingQuestionPlayerName = MY_AVATAR_NAME;
    synchronizer.synch.choosingQuestionPlayerFullName = MY_AVATAR_FIRST_AND_LAST_NAME;
    synchronizer.synch.answeringPlayerName = SECRET_SEPARATOR;
}

function changeMyAvatarScore(isCorrectAnswer, score)
{
    const myAvatarIndex = getMyAvatarIndex();
    const scores = synchronizer.synch.scores.split(SECRET_SEPARATOR);
    scores[myAvatarIndex] = parseInt(scores[myAvatarIndex]) + score * (isCorrectAnswer ? 1 : -1);
    synchronizer.synch.scores = scores.join(SECRET_SEPARATOR);
}

function initTableCubes()
{
    for (var i = 0; i < TABLE_ROWS_COUNT; ++i)
    {
        tableCubes.push([]);
        questionCubes.push([]);
        for (var j = 0; j < TABLE_COLS_COUNT; ++j)
        {
            const index = i * TABLE_COLS_COUNT + j;
            const cube = scene.getObjectByName(TABLE_ITEM_NAME_PREFIX + index);
            tableCubes[i].push(cube);

            if (j > 0)
            {
                questionCubes[i].push(cube);
            }
        }
    }
}

function initTableCubesAngle()
{
    for (var i = 0; i < TABLE_ROWS_COUNT; ++i)
    {
        for (var j = 0; j < TABLE_COLS_COUNT; ++j)
        {
            tableCubes[i][j].rotation = new Rotation3D(0, 0, 1, TABLE_CELL_ROTATE_ANGLE);
        }
    }
}

function initTableCubesPos()
{
    const currPos = TABLE_CELL_START_POS.clone();
    for (var i = TABLE_ROWS_COUNT - 1; i >= 0; --i)
    {
        for (var j = 0; j < TABLE_COLS_COUNT; ++j)
        {
            tableCubes[i][j].position = currPos.clone();
            currPos.y -= TABLE_CELL_SIDE_SIZE;
        }
        currPos.y = TABLE_CELL_START_POS.y;
        currPos.z += TABLE_CELL_SIDE_SIZE;
    }
}

function showWelcomeWindow()
{
    if (synchronizer.synch.gameState != GAME_STATE.ENDED)
    {
        const text = synchronizer.synch.gameState == GAME_STATE.NOT_STARTED ? gameNotStartedWelcomePlayerText : gameStartedWelcomePlayerText;
        showMessageBox(welcomeWindowTitle, text);
    }
}

function initRecordsTable()
{
    scene.RecordsTable.Init(GAME_NAME);
}

function initShowRecordsTableObject()
{
    showRecordsTableObject.onPress = showRecordsTable;
}

function showRecordsTable()
{
    scene.RecordsTable.Show(RECORDS_TABLE_ROWS_COUNT);
}

function initClearRecordsTableObject()
{
    clearRecordsTableObject.onPress = function()
    {
        const inputBox = showInputBox(requestPassToClearRecordsTableWindowTitle, requestPassToClearRecordsTableMessage);
        inputBox.onYes = function()
        {
            const correctPass = sha1(inputBox.getInput()) == CLEAR_RECORDS_TABLE_PASS;
            if (correctPass)
            {
                scene.RecordsTable.Clear();
            }

            const msg = correctPass ? clearRecordsTableSuccessFeedback : clearRecordsTableFailFeedback;
            showMessageBox(clearRecordsTableFeedbackWindowTitle, msg);
        };
    };
}

function initSynchronizer()
{
    if (synchronizer.synch.initializingCapturedAvatar)
    {
        return;
    }

    synchronizer.synch.initializingCapturedAvatar = MY_AVATAR_NAME;
    synchronizer.synch.commonPartInitialized = FALSE;
    synchronizer.synch.finalQuestionAnswerSounded = FALSE;
    synchronizer.synch.finalQuestionIndex = -1;
    synchronizer.synch.finalRoundStarted = FALSE;
    synchronizer.synch.gameState = GAME_STATE.NOT_STARTED;
    synchronizer.synch.answeringPlayerName = SECRET_SEPARATOR;
    synchronizer.synch.choosingQuestionPlayerName = SECRET_SEPARATOR;
    synchronizer.synch.choosingQuestionPlayerFullName = SECRET_SEPARATOR;
    synchronizer.synch.solvedQuestionsCount = 0;
    synchronizer.synch.currRound = 1;
    synchronizer.synch.players = "";
    synchronizer.synch.playersNamesForScoresheet = "";
    synchronizer.synch.playersForRecodsTable = "";
    synchronizer.synch.finalRoundAnsweringPlayers = "";
    synchronizer.synch.scores = "";
    synchronizer.synch.currentQuestionIndexes = SECRET_SEPARATOR;
    synchronizer.synch.ignoreQuestionCubeClick = FALSE;
    synchronizer.synch.solvedQuestionsIndexes = "";
    synchronizer.synch.savedPlayersRecords = "";
    synchronizer.synch.recordsTableHasBeenShownForPlayers = "";
    synchronizer.synch.waitingPlayerReadyAnswerTimerBreakesCount = 0;
}

scene[CHECK_ADD_RESULTS_TO_SCORE_TABLE] = function()
{
    if (synchronizer.synch.gameState == GAME_STATE.ENDED)
    {
        if ((synchronizer.synch.savedPlayersRecords.indexOf(MY_AVATAR_NAME) == -1) && (synchronizer.synch.playersForRecodsTable.indexOf(MY_AVATAR_NAME) != -1))
        {
            if (finalRoundInputBox)
            {
                finalRoundInputBox.close();
            }

            if (receivingAnswerDialogue)
            {
                receivingAnswerDialogue.close();
            }

            if (synchronizer.synch.players.indexOf(MY_AVATAR_NAME) != -1)
            {
                addResultsToRecordsTable();
            }

            const separator = synchronizer.synch.savedPlayersRecords == "" ? "" : SECRET_SEPARATOR;
            synchronizer.synch.savedPlayersRecords += separator + MY_AVATAR_NAME;
        }
    }
    return true;
};

scene[CHECK_SHOW_RECORDS_TABLE_AT_THE_END_OF_GAME] = function()
{
    const allResultsSaved = synchronizer.synch.savedPlayersRecords.split(SECRET_SEPARATOR).length == synchronizer.synch.playersForRecodsTable.split(SECRET_SEPARATOR).length;
    const hasBeenShown = synchronizer.synch.recordsTableHasBeenShownForPlayers.split(SECRET_SEPARATOR).indexOf(MY_AVATAR_NAME) != -1;
    if ((synchronizer.synch.gameState == GAME_STATE.ENDED) && !hasBeenShown && allResultsSaved)
    {
        destroyTimers();

        const separator = synchronizer.synch.recordsTableHasBeenShownForPlayers == "" ? "" : SECRET_SEPARATOR;
        synchronizer.synch.recordsTableHasBeenShownForPlayers += separator + MY_AVATAR_NAME;

        showRecordsTable();
    }
    return true;
};

scene[CHECK_SITTING_PLACES_FUNC] = function()
{
    var players;
    const avatarSittingPlace = getAvatarSittingPlace();
    var needRemove = true;

    players = synchronizer.synch.players.split(SECRET_SEPARATOR);
    for (var i = 0; i < sittingPlaces.length; ++i)
    {
        if (avatarSittingPlace == sittingPlaces[i])
        {
            needRemove = false;
            if (players.indexOf(MY_AVATAR_NAME) == -1)
            {
                const separator = synchronizer.synch.players == "" ? "" : SECRET_SEPARATOR;
                synchronizer.synch.players += separator + MY_AVATAR_NAME;
                synchronizer.synch.playersForRecodsTable =  synchronizer.synch.players;
                synchronizer.synch.scores += separator + 0;
                synchronizer.synch.playersNamesForScoresheet += separator + MY_AVATAR_FIRST_AND_LAST_NAME;
            }
            break;
        }
    }

    players = synchronizer.synch.players.split(SECRET_SEPARATOR);
    const myAvatarIndex = players.indexOf(MY_AVATAR_NAME);
    if (needRemove && myAvatarIndex != -1)
    {
        players.splice(myAvatarIndex, 1);
        synchronizer.synch.players = players.join(SECRET_SEPARATOR);

        var scores = synchronizer.synch.scores.split(SECRET_SEPARATOR);
        scores.splice(myAvatarIndex, 1);
        synchronizer.synch.scores = scores.join(SECRET_SEPARATOR);

        var playersNamesForScoresheet = synchronizer.synch.playersNamesForScoresheet.split(SECRET_SEPARATOR);
        playersNamesForScoresheet.splice(myAvatarIndex, 1);
        synchronizer.synch.playersNamesForScoresheet = playersNamesForScoresheet.join(SECRET_SEPARATOR);
    }

    if ((synchronizer.synch.players != "") && players.length == sittingPlaces.length && synchronizer.synch.gameState == GAME_STATE.NOT_STARTED)
    {
        synchronizer.synch.gameState = GAME_STATE.BEFORE_STARTED;

        anchormanBot.playSound(scene[START_GAME_SOUND_NAME], 100);

        generateQuestionTableImageComposition(questionTextObject, scene[START_GAME_AFTER_15_SEC]);

        startGameTimer = new Timer(START_SOLVE_QUESTIONS_FUNC, BEFORE_START_SOLVE_QUESTIONS_TIME);
        startGameTimer.start();

        updateScoreSheet();
    }

    players = synchronizer.synch.players.split(SECRET_SEPARATOR);
    if (((synchronizer.synch.players == "") || players.length < sittingPlaces.length) && (synchronizer.synch.gameState == GAME_STATE.STARTED || synchronizer.synch.gameState == GAME_STATE.BEFORE_STARTED))
    {
        endGame();
        return true;
    }

    if (((synchronizer.synch.players == "") || players.length == 0) && synchronizer.synch.gameState == GAME_STATE.ENDED)
    {
        synchronizer.synch.initializingCapturedAvatar = null;
        initializeGame();
        return true;
    }

    return true;
};

scene[CHECK_SHOW_START_GAME_MESSAGE] = function()
{
    if (!startingGameMessageBoxIsShown && (synchronizer.synch.gameState == GAME_STATE.BEFORE_STARTED))
    {
        startingGameMessageBoxIsShown = true;
        startingGameMessageBox = showMessageBox(startingGameWindowTitle, startingGameWindowMessage);
    }
    return true;
};

scene[CHECK_RESET_FLAGS] = function()
{
    if (synchronizer.synch.gameState == GAME_STATE.ENDED || synchronizer.synch.gameState == GAME_STATE.NOT_STARTED)
    {
        startingGameMessageBoxIsShown = false;
    }
    return true;
};

scene[CHECK_SHOW_FINAL_QUESTION] = function()
{
    if ((synchronizer.synch.finalRoundStarted == TRUE) && (synchronizer.synch.players.indexOf(MY_AVATAR_NAME) != -1) && (synchronizer.synch.finalRoundAnsweringPlayers.indexOf(MY_AVATAR_NAME) == -1))
    {
        if (needDestroyTimers)
        {
            destroyTimers();
        }

        const separator = synchronizer.synch.finalRoundAnsweringPlayers == "" ? "" : SECRET_SEPARATOR;
        synchronizer.synch.finalRoundAnsweringPlayers += separator + MY_AVATAR_NAME;

        showFinalRoundQuestion();
    }
    return true;
};

function endGame()
{
    if (synchronizer.synch.gameState != GAME_STATE.ENDED)
    {
        synchronizer.synch.gameState = GAME_STATE.ENDED;

        destroyTimers();

        anchormanBot.playSound(scene[END_GAME_SOUND_NAME], 100);

        synchronizer.synch.answeringPlayerName = SECRET_SEPARATOR;
        synchronizer.synch.choosingQuestionPlayerName = SECRET_SEPARATOR;
        synchronizer.synch.choosingQuestionPlayerFullName = SECRET_SEPARATOR;
        synchronizer.synch.currentQuestionIndexes = SECRET_SEPARATOR;
        synchronizer.synch.ignoreQuestionCubeClick = FALSE;

        updateScoreSheet();

        generateQuestionTableImageComposition(questionTextObject, scene[END_OF_GAME_IMAGE]);

        initTexturesForCurrRound(true);
    }
}

function addResultsToRecordsTable()
{
    if (synchronizer.synch.players.indexOf(MY_AVATAR_NAME) != -1)
    {
        const scores = synchronizer.synch.scores.split(SECRET_SEPARATOR);
        scene.RecordsTable.AddRecord(scores[getMyAvatarIndex()])
    }
}

scene[START_SOLVE_QUESTIONS_FUNC] = function()
{
    synchronizer.synch.gameState = GAME_STATE.STARTED;
    synchronizer.synch.choosingQuestionPlayerName = synchronizer.synch.players.split(SECRET_SEPARATOR)[0];
    synchronizer.synch.choosingQuestionPlayerFullName = synchronizer.synch.playersNamesForScoresheet.split(SECRET_SEPARATOR)[0];
    synchronizer.synch.ignoreQuestionCubeClick = FALSE;
    finalRoundCorrectAnswer = null;
    needDestroyTimers = true;

    initTexturesForCurrRound(false);

    generateQuestionTableTextComposition(questionTextObject, getChooseQuestionInvitation());
};

function updateScoreSheet()
{
    if (synchronizer.synch.players == "")
    {
        generateScoreSheetComposition(scoresheet, "");
        return;
    }

    const players = synchronizer.synch.playersNamesForScoresheet.split(SECRET_SEPARATOR);
    const scores = synchronizer.synch.scores.split(SECRET_SEPARATOR);

    const alreadyAdded = [];
    const sequence = [];
    for (var i = 0; i < scores.length; ++i)
    {
        alreadyAdded.push(false);
        sequence.push(parseInt(scores[i]));
    }
    sort(sequence, 0, sequence.length - 1);

    var scoresheetContent = "";
    for (var j = sequence.length - 1; j >= 0; --j)
    {
        var index;
        for (var k = 0; k < scores.length; ++k)
        {
            if ((sequence[j] == parseInt(scores[k])) && !alreadyAdded[k])
            {
                alreadyAdded[k] = true;
                index = k;
                break;
            }
        }
        var playerName = players[index];
        if (playerName.length > SCORESHEET_MAX_PLAYER_NAME_LENGTH)
        {
            playerName = playerName.substr(0, SCORESHEET_MAX_PLAYER_NAME_LENGTH) + "...";
        }
        scoresheetContent += playerName + " " + scores[index] + "<br /><br />";
    }

    generateScoreSheetComposition(scoresheet, scoresheetContent);
}

function generateScoreSheetComposition(object, text)
{
    const c = createComposition();
    createColorRect(0, 0, TEXTURES_SIZE, TEXTURES_SIZE, "#2B2DB4", c);
    createText(10, 50, "Arial", 20, false, false, false, "#FFFF00", text, c);
    object.replaceByGeneratedTexture(4, TEXTURES_SIZE, TEXTURES_SIZE, c);
}

function generateQuestionTableTextComposition(object, text)
{
    const c = createComposition();
    createColorRect(0, 0, TEXTURES_SIZE, TEXTURES_SIZE, "#2B2DB4", c);
    createText(20, 50, "Arial", QUESTION_TEXT_FONT_SIZE, false, false, false, "#FFFFFF", text.replace(QUESTION_TEXT_PATTERN, "$1<br />"), c);
    object.replaceByGeneratedTexture(4, TEXTURES_SIZE, TEXTURES_SIZE, c);
}

function generateQuestionTableImageComposition(object, image)
{
    const c = createComposition();
    imageFromServerResource(0, 0, image, c);
    object.replaceByGeneratedTexture(4, TEXTURES_SIZE, TEXTURES_SIZE, c);
}

function generateQuestionCubeTexture(object, text, col)
{
    const c = createComposition();

    const padding = 0;
    const lineWidth = 8;
    createColorRect(0, 0, TEXTURES_SIZE, TEXTURES_SIZE, "#2B2DB4", c);
    createLine(padding, padding, TEXTURES_SIZE - padding, padding, lineWidth, "#FFFFFF", c);
    createLine(TEXTURES_SIZE - padding, padding, TEXTURES_SIZE - padding, TEXTURES_SIZE - padding, lineWidth, "#FFFFFF", c);
    createLine(TEXTURES_SIZE - padding, TEXTURES_SIZE - padding, padding, TEXTURES_SIZE - padding, lineWidth, "#FFFFFF", c);
    createLine(padding, TEXTURES_SIZE - padding, padding, padding, lineWidth, "#FFFFFF", c);

    createText(col < 3 ? 75 : 40, 85, "Arial", 100, false, false, false, "#FFFFFF", text, c);
    object.replaceByGeneratedTexture(0, TEXTURES_SIZE, TEXTURES_SIZE, c);
}

function generateThemeCubeTexture(object, image)
{
    const c = createComposition();

    const lineWidth = 8;
    createColorRect(0, 0, TEXTURES_SIZE, TEXTURES_SIZE, "#2B2DB4", c);
    createLine(TEXTURES_SIZE, TEXTURES_SIZE, 0, TEXTURES_SIZE, lineWidth, "#FFFFFF", c);
    if (image)
    {
        imageFromServerResource(0, 0, image, c);
    }
    object.replaceByGeneratedTexture(0, TEXTURES_SIZE, TEXTURES_SIZE, c);
}

// utils

function isValidNumber(numberAsString)
{
    var valid = true;
    for (var i = 0; i < numberAsString.length; ++i)
    {
        var currDigit = numberAsString[i];
        if (currDigit < "0" || currDigit > "9")
        {
            valid = false;
            break;
        }
    }
    return valid;
}

function getRandom()
{
    return Math.round(Math.random() * 1000);
}

function getMyAvatarFirstAndLastName()
{
    var myAvatar = scene.getMyAvatar();
    var firstNandLastName = null;
    if (myAvatar != null)
    {
        const info = myAvatar.getAvatarInfo();
        firstNandLastName = info.lastname + " " + info.firstname;
    }
    return firstNandLastName;

}

function getMyAvatarName()
{
    var myAvatar = scene.getMyAvatar();
    var login = null;
    if (myAvatar != null)
    {
        login = myAvatar.getAvatarInfo().login;
    }
    return login;
}

function getMyAvatarIndex()
{
    return synchronizer.synch.players.split(SECRET_SEPARATOR).indexOf(MY_AVATAR_NAME);
}

function getAvatarSittingPlace()
{
    var myAvatar = scene.getMyAvatar();
    var avatarSittingPlace = null;
    if (myAvatar != null)
    {
        avatarSittingPlace = myAvatar.getAttachParent();
    }
    return avatarSittingPlace;
}

function isIgnoreClickOnQuestionCube(i, j)
{
    return (synchronizer.synch.currentQuestionIndexes != SECRET_SEPARATOR) ||
        (synchronizer.synch.gameState != GAME_STATE.STARTED) ||
        (synchronizer.synch.answeringPlayerName != SECRET_SEPARATOR) ||
        (synchronizer.synch.choosingQuestionPlayerName != MY_AVATAR_NAME) ||
        (synchronizer.synch.solvedQuestionsIndexes.indexOf([i, j].join(SOLVED_QUESTION_COORDS_SEPARATOR)) != -1) ||
        isSpectatorAvatarClick();
}

function isSpectatorAvatarClick()
{
    return synchronizer.synch.players.split(SECRET_SEPARATOR).indexOf(MY_AVATAR_NAME) == -1;
}

function isNeedVisualElementsInitialize()
{
    return (synchronizer.synch.commonPartInitialized == FALSE) && synchronizer.synch.initializingCapturedAvatar == MY_AVATAR_NAME;
}

function getCurrQuestionsGroups()
{
    return synchronizer.synch.currRound == MAIN_ROUNDS_COUNT ? round2QuestionsGroups : round1QuestionsGroups;
}

function getCurrThemesImages()
{
    return synchronizer.synch.currRound == MAIN_ROUNDS_COUNT ? ROUND2_THEMES : ROUND1_THEMES;
}

function merge(arr, min, mid, max)
{
    var firstIndex = min;
    var secondIndex = mid + 1;
    var sortedPart = [];

    while((firstIndex <= mid) && (secondIndex <= max))
    {
        if (arr[firstIndex] <= arr[secondIndex])
        {
            sortedPart.push(arr[firstIndex++])
        }
        else
        {
            sortedPart.push(arr[secondIndex++])
        }
    }

    for (var i = firstIndex; i <= mid; ++i)
    {
        sortedPart.push(arr[i])
    }

    for (var j = secondIndex; j <= max; ++j)
    {
        sortedPart.push(arr[j])
    }

    for (var k = min, m = 0; k <= max; ++k, ++m)
    {
        arr[k] = sortedPart[m];
    }
}

function sort(arr, min, max)
{
    if (min < max)
    {
        var mid = Math.floor((max + min) / 2);

        sort(arr, min, mid);
        sort(arr, mid + 1, max);

        merge(arr, min, mid, max);
    }
}

// external lib

function sha1(str) {
    var rotate_left = function(n, s) {
        var t4 = (n << s) | (n >>> (32 - s));
        return t4;
    };

    var cvt_hex = function(val) {
        var str = '';
        var i;
        var v;

        for (i = 7; i >= 0; i--) {
            v = (val >>> (i * 4)) & 0x0f;
            str += v.toString(16);
        }
        return str;
    };

    var blockstart;
    var i, j;
    var W = new Array(80);
    var H0 = 0x67452301;
    var H1 = 0xEFCDAB89;
    var H2 = 0x98BADCFE;
    var H3 = 0x10325476;
    var H4 = 0xC3D2E1F0;
    var A, B, C, D, E;
    var temp;

    str = unescape(encodeURIComponent(str));
    var str_len = str.length;

    var word_array = [];
    for (i = 0; i < str_len - 3; i += 4) {
        j = str.charCodeAt(i) << 24 | str.charCodeAt(i + 1) << 16 | str.charCodeAt(i + 2) << 8 | str.charCodeAt(i + 3);
        word_array.push(j);
    }

    switch (str_len % 4) {
        case 0:
            i = 0x080000000;
            break;
        case 1:
            i = str.charCodeAt(str_len - 1) << 24 | 0x0800000;
            break;
        case 2:
            i = str.charCodeAt(str_len - 2) << 24 | str.charCodeAt(str_len - 1) << 16 | 0x08000;
            break;
        case 3:
            i = str.charCodeAt(str_len - 3) << 24 | str.charCodeAt(str_len - 2) << 16 | str.charCodeAt(str_len - 1) <<
                8 | 0x80;
            break;
    }

    word_array.push(i);

    while ((word_array.length % 16) != 14) {
        word_array.push(0);
    }

    word_array.push(str_len >>> 29);
    word_array.push((str_len << 3) & 0x0ffffffff);

    for (blockstart = 0; blockstart < word_array.length; blockstart += 16) {
        for (i = 0; i < 16; i++) {
            W[i] = word_array[blockstart + i];
        }
        for (i = 16; i <= 79; i++) {
            W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
        }

        A = H0;
        B = H1;
        C = H2;
        D = H3;
        E = H4;

        for (i = 0; i <= 19; i++) {
            temp = (rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
            E = D;
            D = C;
            C = rotate_left(B, 30);
            B = A;
            A = temp;
        }

        for (i = 20; i <= 39; i++) {
            temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
            E = D;
            D = C;
            C = rotate_left(B, 30);
            B = A;
            A = temp;
        }

        for (i = 40; i <= 59; i++) {
            temp = (rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
            E = D;
            D = C;
            C = rotate_left(B, 30);
            B = A;
            A = temp;
        }

        for (i = 60; i <= 79; i++) {
            temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
            E = D;
            D = C;
            C = rotate_left(B, 30);
            B = A;
            A = temp;
        }

        H0 = (H0 + A) & 0x0ffffffff;
        H1 = (H1 + B) & 0x0ffffffff;
        H2 = (H2 + C) & 0x0ffffffff;
        H3 = (H3 + D) & 0x0ffffffff;
        H4 = (H4 + E) & 0x0ffffffff;
    }

    temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);
    return temp.toLowerCase();
}

// load questions base

function initRoundQuestions(questionGroups, roundQuestions, roundAnswers, isFirstRound)
{
    for (var i = 0; i < TABLE_ROWS_COUNT; ++i)
    {
        const theme = new Theme();
        for (var j = 0; j < QUESTIONS_FIELD_COLS_COUNT; ++j)
        {
            theme.questions.push([]);
            for (var k = 0; k < QUESTION_COUNT_FOR_EACH_SCORE; ++k)
            {
                const question = new Question();
                const questionData = roundQuestions[i][j][k];
                question.text = questionData[0];
                question.isChoosen = false;
                question.score = (j + 1) * SCORE_STEP;

                const questionSoundName = "nt" + (i + 1 + (isFirstRound ? 0 : TABLE_ROWS_COUNT)) + "q" + (j * QUESTION_COUNT_FOR_EACH_SCORE + k + 1);
                question.sound = scene[questionSoundName];
                question.soundDuration = questionData[1];

                const answers = roundAnswers[i][j][k];
                for (var m = 0; m < answers.length; ++m)
                {
                    const answer = new Answer();
                    const answerInfo = answers[m];
                    answer.text = answerInfo[0] + (DEBUG_MODE && answerInfo[1] ? "---CORRECT---" : "");
                    answer.isCorrect = answerInfo[1];
                    answer.sound = scene[questionSoundName + "a" + (m + 1)];

                    question.answers.push(answer);
                }

                theme.questions[j].push(question);
            }
        }
        questionGroups.push(theme);
    }
}

function initFinalRoundQuestions()
{
    for (var i = 0; i < FINAL_ROUND_QUESTIONS.length; ++i)
    {
        const question = new Question();
        const questionData = FINAL_ROUND_QUESTIONS[i];
        question.text = questionData[0];
        question.isChoosen = false;

        const questionSoundName = "f" + (i + 1);
        question.sound = scene[questionSoundName];
        question.soundDuration = questionData[1];

        const answers = FINAL_ROUND_ANSWERS[i];
        for (var j = 0; j < answers.length; ++j)
        {
            const answer = new Answer();
            const answerInfo = answers[j];
            answer.text = answerInfo[0] + (DEBUG_MODE && answerInfo[1] ? "---CORRECT---" : "");
            answer.isCorrect = answerInfo[1];
            answer.sound = scene[questionSoundName + "a" + (j + 1)];
            answer.soundDuration = answerInfo[2];

            question.answers.push(answer);
        }

        finalRoundQuestions.push(question);
    }
}